package com.fileshare.web.service.impl;

import com.fileshare.web.constant.ActionConstant;
import com.fileshare.web.dao.UserActionRepository;
import com.fileshare.web.dto.info.LoginInfo;
import com.fileshare.web.entity.User;
import com.fileshare.web.entity.UserAction;
import com.fileshare.web.properties.Oauth2GiteeProperties;
import com.fileshare.web.properties.Oauth2QqProperties;
import com.fileshare.web.service.AuthService;
import com.fileshare.web.service.LoginService;
import com.fileshare.web.service.UserService;
import com.fileshare.web.utils.Md5Util;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthGiteeRequest;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRequest;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;

import static com.fileshare.web.constant.WebConst.LOGIN_INFO;

@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    private Oauth2GiteeProperties giteeProperties;
    @Resource
    private Oauth2QqProperties qqProperties;
    @Resource
    private AuthService authService;
    @Resource
    private UserService userService;
    @Resource
    private UserActionRepository userActionRepository;

    @Override
    public AuthRequest getAuthRequest(String platform) {
        AuthRequest authRequest = null;
        switch (platform.toLowerCase()) {
            case "gitee":
                authRequest = new AuthGiteeRequest(AuthConfig.builder()
                        .clientId(giteeProperties.getClientId())
                        .clientSecret(giteeProperties.getClientSecret())
                        .redirectUri(giteeProperties.getRedirectUri())
                        .build());
                break;
            case "qq":
                authRequest = new AuthQqRequest(AuthConfig.builder()
                        .clientId(qqProperties.getClientId())
                        .clientSecret(qqProperties.getClientSecret())
                        .redirectUri(qqProperties.getRedirectUri())
                        .build());
                break;
            default:
                break;
        }
        if (null == authRequest) {
            throw new AuthException("未配置或配置出错");
        }
        return authRequest;
    }

    @Override
    @Transactional
    public boolean thirdPartyLogin(String source, AuthCallback callback, HttpServletRequest request) {
        try {
            AuthRequest authRequest = getAuthRequest(source);
            AuthResponse<AuthUser> authResponse = authRequest.login(callback);
            AuthUser authUser = authResponse.getData();
            User thirdPartyUser = userService.queryThirdPartyUser(source, authUser.getUuid());
            if (thirdPartyUser == null) {
                thirdPartyUser = generateUser(source, authUser);
                userService.addUser(thirdPartyUser);
                authService.initThirdPartyUserAuth(thirdPartyUser);
                addUserLoginLog(thirdPartyUser, ActionConstant.USER_ADD);
            }
            addUserLoginLog(thirdPartyUser, ActionConstant.USER_LOGIN);
            handleLoginInfoAndAuth(request, thirdPartyUser);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public void loginOut(HttpServletRequest request) {
        LoginInfo loginInfo = getLoginInfo(request);
        if (loginInfo != null) {
            addUserLoginLog(loginInfo.getUser(), ActionConstant.USER_LOGOUT);
        }
        request.getSession().setAttribute(LOGIN_INFO, null);
        SecurityContextHolder.getContext().setAuthentication(null);
    }

    @Override
    public boolean loginBefore(HttpServletRequest request) {
        Object attribute = request.getSession().getAttribute(LOGIN_INFO);
        return attribute instanceof LoginInfo;
    }

    private User generateUser(String source, AuthUser authUser) {
        User user = new User();
        user.setAvatar(authUser.getAvatar());
        user.setNickName(authUser.getNickname());
        user.setThirdPartySeq(authUser.getUuid());
        user.setUserName(authUser.getUsername());
        user.setUserType(source);
        user.setGender(authUser.getGender().getDesc());
        user.setPassword("");
        user.setSalt("");
        user.setUserSeq(userService.generateUserSeq());
        user.setEmail(authUser.getEmail());
        return user;
    }

    @Override
    @Transactional
    public boolean webLogin(String username, String password, HttpServletRequest request) {
        User dbUser = userService.getUserByUserName(username);
        if (compareWithDb(dbUser, password)) {
            handleLoginInfoAndAuth(request, dbUser);
            addUserLoginLog(dbUser, ActionConstant.USER_LOGIN);
            return true;
        }
        return false;
    }

    @Override
    public void addUserLoginLog(User dbUser, String actionType) {
        UserAction action = new UserAction();
        action.setActionType(actionType);
        String userSeq = dbUser.getUserSeq();
        Date now = new Date();
        action.setCreateAt(now);
        action.setUpdateAt(now);
        action.setCreateBy(userSeq);
        action.setIpAddress("");
        action.setUpdateBy(userSeq);
        userActionRepository.addActionLog(action);
    }

    @Override
    public LoginInfo getLoginInfo(HttpServletRequest request) {
        Object attribute = request.getSession().getAttribute(LOGIN_INFO);
        if (attribute instanceof LoginInfo) {
            return (LoginInfo) attribute;
        }
        return null;
    }

    private void handleLoginInfoAndAuth(HttpServletRequest request, User user) {
        LoginInfo info = LoginInfo.builder()
                .authorities(getUserAuthorities(user))
                .user(user)
                .build();
        request.getSession().setAttribute(LOGIN_INFO, info);
    }

    private List<String> getUserAuthorities(User dbUser) {
        return authService.getUserAuthStrings(dbUser);
    }

    private boolean compareWithDb(User dbUser, String password) {
        /**dbUser的password是经过MD5Util.
         generate(password,salt)加密后得到的
         储存用户信息时候，MD5Util下面有个自动生成salt的方法
         **/
        return Md5Util.verify(password, dbUser.getPassword());
    }
}
